home *** CD-ROM | disk | FTP | other *** search
/ ...taking it to the Macs! / ...taking it to the Macs!.iso / Extras / ActiveX Mac SDK / ActiveX SDK / Sample Controls / Gradient / CGradientControl.cp < prev    next >
Encoding:
Text File  |  1996-12-20  |  23.6 KB  |  754 lines  |  [TEXT/CWIE]

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // include's
  4. //
  5.  
  6. #include "ocheaders.h"
  7. #include "CBaseControl.h"
  8. #include "CErrorControl.h"
  9. #include "CGradientControl.h"
  10. #include "Colors.h"
  11. #include "FnAssert.h"
  12. #include "BDAssert.h"
  13. #include "BDVerify.h"
  14. #include "BDUtils.h"
  15. #include "dispatch.h"
  16. #include "CError.h"
  17. #include "CGradientError.h"
  18. #include <ctype.h>
  19. #include <math.h>
  20. #include <stdio.h>
  21. #include <algobase.h>
  22.  
  23. ///////////////////////////////////////////////////////////////////////////////
  24. //
  25. //  CGradientControl::CGradientControl
  26. //
  27. //    Constructor
  28. //
  29.  
  30. CGradientControl::CGradientControl(void) : CBaseControl()
  31. {    
  32. // protected:
  33.     mMainOffscreenGWorld     = nil;
  34.     mPixOffscreenPixMap     = nil;
  35.     mRectOffscreenBounds     = gZeroRect;
  36.     mDirection                = Horizontal;
  37.     mStartColor                = RGB_WHITE;
  38.     mEndColor                = RGB_BLACK;
  39. #if defined (start_end_point)
  40.     mStartPoint                = gZeroPoint;
  41.     mEndPoint                = gZeroPoint;
  42. #endif
  43. }
  44.  
  45. ///////////////////////////////////////////////////////////////////////////////
  46. //
  47. //  CGradientControl::~CGradientControl
  48. //
  49. //    Destructor
  50. //
  51.  
  52. CGradientControl::~CGradientControl(void)
  53. {    
  54.     // clean up offscreen world
  55.     if (mMainOffscreenGWorld != nil)
  56.     {
  57.         DisposeGWorld(mMainOffscreenGWorld);
  58.         mMainOffscreenGWorld = nil;
  59.         mPixOffscreenPixMap = nil;
  60.     }
  61. }
  62.  
  63. ///////////////////////////////////////////////////////////////////////////////
  64. //
  65. //  CGradientControl::IUnknown::QueryInterface
  66. //
  67. //  Returns a pointer to the specified interface on a component to which a
  68. //  client currently holds an interface pointer.
  69. //
  70.  
  71. STDMETHODIMP
  72. CGradientControl::QueryInterface(REFIID inRefID, void** outObj)
  73. {
  74.     if (inRefID == IID_IBindStatusCallback) 
  75.         return CBaseBindStatusCallback::QueryInterface(inRefID, outObj);
  76.     else
  77.         return CBaseControl::QueryInterface(inRefID, outObj);
  78. }
  79.  
  80. ///////////////////////////////////////////////////////////////////////////////
  81. //
  82. //  CGradientControl::IInProcObject::Draw
  83. //
  84. //    Context            the drawing context for ActiveX - includes the port
  85. //
  86. //    Draw the Gradient object.
  87. //
  88.  
  89. STDMETHODIMP
  90. CGradientControl::Draw(THIS_ DrawContext* Context)
  91. {
  92.     if (Context->DrawAspect != DVASPECT_CONTENT)
  93.         return ResultFromScode(DV_E_DVASPECT);
  94.         
  95.     this->PrepareOffscreenWorld(Context);    // get the main offscreen world setup and ready
  96.  
  97.     // get the offscreen bitmap
  98.     BitMap* offscreenBitMap = this->GetOffscreenBitMap();
  99.     
  100.     // draw the Gradient
  101.     this->RenderGradientControl(mRectOffscreenBounds);
  102.     
  103.     // Move it
  104.     this->MoveOnScreen(offscreenBitMap, Context);
  105.     
  106.     // unlock, and unload
  107.     this->ReleaseOffscreenBitMap(offscreenBitMap);
  108.  
  109.     return ResultFromScode(S_OK);
  110. }
  111.  
  112. ///////////////////////////////////////////////////////////////////////////////
  113. //
  114. //  CGradientControl::IPersistPropertyBag::Load
  115. //
  116. //    PropBag            container that holds all the properties passed in
  117. //    ErrorLog        ptr to an error log
  118. //
  119. //    Sets the data members that represent the user definable properties from the URL.
  120. //
  121.  
  122. STDMETHODIMP
  123. CGradientControl::Load(IPropertyBag* PropBag, IErrorLog* ErrorLog)
  124. {
  125.     SCODE theSCode = LoadTextState(PropBag, ErrorLog);
  126.     return ResultFromScode(theSCode);
  127. }
  128.  
  129. ///////////////////////////////////////////////////////////////////////////////
  130. //
  131. // CGradientControl::IPersistPropertyBag::LoadTextState
  132. //
  133. //    pPropertyBag    container that holds all the properties passed in
  134. //    pErrorLog        ptr to an error log
  135. //
  136. //    Sets the data members that represent the user definable properties from the container stream.
  137. //
  138.  
  139. STDMETHODIMP CGradientControl::LoadTextState(IPropertyBag *pPropertyBag,IErrorLog *pErrorLog)
  140. {
  141. //    const long    maxLength = 256;
  142.     char        propertyString[maxPropertyStringLength];
  143.  
  144.     // try to load in each property.  if we can't get it, then leave
  145.     // things at the default.
  146.  
  147.     // The gradient direction
  148.     if (::LoadPropertyString(pPropertyBag, "direction", propertyString, maxPropertyStringLength, pErrorLog))
  149.         mDirection = (enumDirection)(atoi(propertyString));
  150.  
  151.     // The end gradient color
  152.     if (::LoadPropertyString(pPropertyBag, "endcolor", propertyString, maxPropertyStringLength, pErrorLog))
  153.         this->LoadColor(&mEndColor, propertyString);
  154.  
  155.     // The start gradient color
  156.     if (::LoadPropertyString(pPropertyBag, "startcolor", propertyString, maxPropertyStringLength, pErrorLog))
  157.         this->LoadColor(&mStartColor, propertyString);
  158.  
  159. #if defined (start_end_point)
  160.     // The end point
  161.     if (::LoadPropertyString(pPropertyBag, "startpoint", propertyString, maxPropertyStringLength, pErrorLog))
  162.         mStartPoint = atoi(propertyString);
  163.  
  164.     // The begin point
  165.     if (::LoadPropertyString(pPropertyBag, "endpoint", propertyString, maxPropertyStringLength, pErrorLog))
  166.         mEndPoint = atoi(propertyString);
  167. #endif
  168.     return ResultFromScode(S_OK);
  169. }  
  170.  
  171. ///////////////////////////////////////////////////////////////////////////////
  172. //
  173. // CGradientControl::LoadColor
  174. //
  175. //
  176. //    theColor        an RGBColor that defines the color tuple exactly (i.e., not indexed)
  177. //    colorString        a string with a color in it repesented in ASCII
  178. //
  179. //    Sets the color data member based on the value of the user definable property.
  180. //
  181.  
  182. Boolean CGradientControl::LoadColor(RGBColor * theColor, char * colorString)
  183. {
  184.     Boolean didSet = true;
  185.     
  186.     for (short i = 0; i < strlen(colorString); i++)
  187.         tolower(colorString[i]);
  188.     
  189.     if (streq(colorString, "black"))
  190.         *theColor = RGB_BLACK;
  191.     else if (streq(colorString, "white"))
  192.         *theColor = RGB_WHITE;
  193.     else if (streq(colorString, "red"))
  194.         *theColor = RGB_RED;
  195.     else if (streq(colorString, "green"))
  196.         *theColor = RGB_GREEN;
  197.     else if (streq(colorString, "blue"))
  198.         *theColor = RGB_BLUE;
  199.     else if (streq(colorString, "cyan"))
  200.         *theColor = RGB_CYAN;
  201.     else if (streq(colorString, "magenta"))
  202.         *theColor = RGB_MAGENTA;
  203.     else if (streq(colorString, "yellow"))
  204.         *theColor = RGB_YELLOW;
  205.     else
  206.     {
  207.         didSet = false;    // reverse Boolean logic -- false unless we succeed
  208.         
  209.         // We're expecting a 32-bit hex RGB value in the format #00bbggrr.  
  210.         //
  211.         // NOTE: To be compatible with Windows, if the high-order bit is set,
  212.         // the low-order byte is supposed to be treated as a system color index.
  213.         // We'll punt on this for now.
  214.         
  215.         Boolean punt = false;
  216.         
  217.         // sanity check on string format.  #bbggrr, #0bbggrr, or #00bbggrr.
  218.         int len = strlen(colorString);
  219.         if ((colorString[0] == '#') && (len >= 7) && (len <= 9))
  220.         {
  221.             // if len == 9, the input may be trying to set the high-order
  222.             // bit.  Check for this and punt if so.
  223.             if (len == 9)
  224.             {
  225.                 char * sHi = "0x??";
  226.                 short iHi = 0;
  227.                 
  228.                 strncat(sHi, colorString, 2);
  229.                 sscanf(sHi, "%hx", &iHi);
  230.                 
  231.                 punt = iHi & 0x40;
  232.             }
  233.                 
  234.             if (!punt) // if we're NOT setting hi-order bit...
  235.             {
  236.                 const int mostSignificantBits = 8;     // amount to shift to move a byte
  237.                 const int numComponents = 3;        // number of array components - one ea for R,G & B
  238.                 const int redPos = 2;                // array position for component
  239.                 const int greenPos = 1;                // array position for component
  240.                 const int bluePos = 0;                // array position for component
  241.  
  242.                 unsigned short desiredColorArray[numComponents]; // a COLORREF, as an array
  243.                 
  244.                 for (short i = 0, pos = len-2; i < numComponents; i++, pos -= 2)
  245.                 {
  246.                     char * sColor = "0x??";
  247.                     short aShort = 0;
  248.                     strncpy(&sColor[2], &colorString[pos], 2);
  249.                     sscanf(sColor, "%hx", &aShort);
  250.                     ASSERT((aShort <= 0xff), "Range check error!");
  251.                     desiredColorArray[i] = aShort << mostSignificantBits;// move the color component value into the ms bits
  252.                 }
  253.                 
  254.                 // return the color that we went to all that trouble to set
  255.                 theColor->red = (unsigned short) desiredColorArray[redPos];
  256.                 theColor->green = (unsigned short) desiredColorArray[greenPos];
  257.                 theColor->blue = (unsigned short) desiredColorArray[bluePos];
  258.  
  259.                 // success, so tell the world
  260.                 didSet = true;
  261.             }
  262.         } 
  263.     }
  264.         
  265.     return didSet;
  266. }
  267.  
  268. ///////////////////////////////////////////////////////////////////////////////
  269. //
  270. //  CGradientControl::PrepareOffscreenWorld
  271. //
  272. //    Context            the drawing context for ActiveX - includes the port
  273. //
  274. //  Creates the offscreen gworld that is the current display.  
  275. //
  276.  
  277. void CGradientControl::PrepareOffscreenWorld(const DrawContext* Context) // MMF
  278. {
  279.     try
  280.     {
  281.         // get the current drawing environment
  282.         
  283.         // If there's already an offscreen GWorld, get rid of it.  
  284.         if (mMainOffscreenGWorld != nil)
  285.         {
  286.             ::DisposeGWorld(mMainOffscreenGWorld);
  287.             mMainOffscreenGWorld = nil;
  288.         }
  289.  
  290.         // Define the size of the GWorld's bounding boxes -- same size as onscreen
  291.         short rightBounds = Context->Location.right - Context->Location.left;
  292.         short bottomBounds = Context->Location.bottom - Context->Location.top;
  293.         ::SetRect(&mRectOffscreenBounds, 0, 0, rightBounds, bottomBounds);
  294.  
  295.         // Allocate a new GWorld for the offscreen drawing and store its PixMap.
  296.         GDHandle currDevice = ::GetGDevice();
  297.         short targetDepth = (**(**currDevice).gdPMap).pixelSize;    // how many pixels deep are we?
  298.         QDErr err = ::NewGWorld(&mMainOffscreenGWorld, thousandsOfColorsDepth, &mRectOffscreenBounds, 0, nil, 0);
  299.  
  300.         if (!err)
  301.         {
  302.             mPixOffscreenPixMap = ::GetGWorldPixMap(mMainOffscreenGWorld);
  303.                         
  304.             // we should now have a gworld
  305.             if (mMainOffscreenGWorld == nil || mPixOffscreenPixMap == nil)
  306.                 throw CGradientError(CONTROL_GWORLD_ALLOCATION_ERROR, this);
  307.         }
  308.     }
  309.     
  310.     // error handling
  311.     catch (CGradientError &GradientError)
  312.     {
  313.         GradientError.HandleError();
  314.     }
  315. }
  316.  
  317. ///////////////////////////////////////////////////////////////////////////////
  318. //
  319. //  CGradientControl::MoveOnScreen
  320. //
  321. //    offscreenBitMap    a PixMap or BitMap pointer
  322. //    Context            the drawing context for ActiveX - includes the port
  323. //
  324. //    Computes the size of the rect needed to hold the rotated rect.
  325. //
  326.  
  327. void CGradientControl::MoveOnScreen(const BitMap * offscreenBitMap, const DrawContext* Context) // MMF
  328. {
  329.     // Set the colors
  330.     ::RGBForeColor(&RGB_BLACK);
  331.     ::RGBBackColor(&RGB_WHITE);
  332.         
  333.     // actually move the bits
  334.     ::CopyBits(    offscreenBitMap, &(Context->Port->portBits), 
  335.                 &mRectOffscreenBounds, &Context->Location, 
  336.                 srcCopy + ditherCopy, nil);
  337. }
  338.  
  339. ///////////////////////////////////////////////////////////////////////////////
  340. //
  341. //  CGradientControl::GetOffscreenBitMap
  342. //
  343. //    returns BitMap    a PixMap or BitMap pointer
  344. //
  345. //    Returns a BitMap/PixMap that is part of the offscreen world - ready to draw into.
  346. //
  347.  
  348. BitMap * CGradientControl::GetOffscreenBitMap(void)
  349. {
  350.     // sanity check
  351.     assert((mPixOffscreenPixMap != nil));
  352.     
  353.     // Lock n load the pixMap handle 'till we're done with it
  354.     ::LockPixels(mPixOffscreenPixMap);
  355.     
  356.     // lock handle so it does not float on us
  357.     ::HLock((Handle)mPixOffscreenPixMap);
  358.     
  359.     // Note the bitmap, now that we've locked n loaded    
  360.     BitMap* offscreenBitMap = (BitMap *)(*mPixOffscreenPixMap);
  361.     assert((offscreenBitMap != nil));
  362.     
  363.     return offscreenBitMap;
  364. }
  365.  
  366. ///////////////////////////////////////////////////////////////////////////////
  367. //
  368. //  CGradientControl::ReleaseOffscreenBitMap
  369. //
  370. //    offscreenBitMap    an offscreenBitMap that we are through with and would like to purge.
  371. //
  372. //    Unlocks handles and pixels in preparation for disposing of a GWorld
  373. //
  374.  
  375. void CGradientControl::ReleaseOffscreenBitMap(BitMap * offscreenBitMap)
  376. {
  377.     
  378.     // we are done with this handle so let it float
  379.     ::HUnlock((Handle)mPixOffscreenPixMap);
  380.     
  381.     // Release the pixMap handle
  382.     ::UnlockPixels(mPixOffscreenPixMap);
  383.     
  384.     // Set this to nil just as a safety factor
  385.     offscreenBitMap = nil;
  386. }
  387.  
  388. ///////////////////////////////////////////////////////////////////////////////
  389. //
  390. //    CGradientControl::RenderGradientControl
  391. //
  392. //    No params        no parameters are passed or returned
  393. //
  394. //    Actually does the drawing of the Gradient. It creates the content GWorld. Then
  395. //    rotates it as needed. Then blits it into the offscreen PixMap (data member).
  396. //
  397.  
  398. void CGradientControl::RenderGradientControl(const Rect contentRect)
  399. {
  400.     QDErr theErr;
  401.     try
  402.     {
  403.         GWorldPtr contentGWorld = nil;    // init cause were anal
  404.  
  405.         GDHandle currDevice = ::GetGDevice();
  406.         short targetDepth = (**(**currDevice).gdPMap).pixelSize;    // how many pixels deep are we?
  407.  
  408.         // alloc the output GWorld and save results of failure
  409.  
  410.         // create content PixMap:
  411.         theErr = ::NewGWorld(&contentGWorld, targetDepth, &contentRect, 0, nil, 0);
  412.         assert(theErr == noErr);
  413.         if(theErr != noErr)
  414.             throw CGradientError(GWORLD_ALLOCATION_ERROR, this);
  415.  
  416.         // Store the current port and device before switching to the offscreen world.
  417.         CGrafPtr    currentPort;
  418.         GDHandle    currentDevice;
  419.         ::GetGWorld(¤tPort, ¤tDevice);
  420.  
  421.         // Switch to the content GWorld and lock the offscreen buffer in memory.
  422.         ::SetGWorld(contentGWorld, nil);
  423.         PixMapHandle contentPixMap = ::GetGWorldPixMap(contentGWorld);
  424.         ::LockPixels(contentPixMap);
  425.  
  426.         ::RGBBackColor(&mEndColor);
  427.         // pre erase the content so that it is ready to go:
  428.         ::EraseRect(&contentRect);
  429.         
  430.         unsigned short maxRed = maxRGBComponentValue;
  431.         unsigned short maxGreen = maxRGBComponentValue;
  432.         unsigned short maxBlue = maxRGBComponentValue;
  433.  
  434.         short width = contentRect.right - contentRect.left;        // compute the width of the rect
  435.         short height = contentRect.bottom - contentRect.top;    // compute the height of the rect
  436.  
  437.         RGBColor theColor = {0, 0, 0};    // the current color in the gray ramp
  438.         Rect sliceRect = gZeroRect;        // init to zero cause were anal
  439.         unsigned short colorSlice = 0;    // mo ditto
  440.         unsigned short sliceNum = 0;    // ditto
  441.                 
  442.         switch(mDirection)
  443.         {
  444.             case Horizontal:
  445.             {
  446.                 // Draw now that things are ready to go:
  447.                 colorSlice = (unsigned short)(maxRGBComponentValue/width);// the amount to incr by for each slice of the gradient
  448.                 const short sliceWidth = 1;
  449.  
  450.                 for(sliceNum = 0; sliceNum < width; sliceNum++)
  451.                 {
  452.                     theColor.red = sliceNum * colorSlice;
  453.                     theColor.green = sliceNum * colorSlice;
  454.                     theColor.blue = sliceNum * colorSlice;
  455.  
  456.                     ::RGBForeColor(&theColor);
  457.  
  458.                     ::SetRect(&sliceRect, (short)sliceNum, 0, sliceNum + sliceWidth, height);
  459.                     assert(ValidRect(sliceRect));
  460.  
  461.                     ::PaintRect(&sliceRect);    // paint the gray transform filter
  462.                 }
  463.                 // restore orig gworld
  464.                 ::SetGWorld(currentPort, nil);
  465.  
  466.                 ::RGBForeColor(&mStartColor);
  467.                 ::RGBBackColor(&mEndColor);
  468.  
  469.                 ::CopyBits((BitMap *)(*contentPixMap), (BitMap *)(*mPixOffscreenPixMap), 
  470.                             &(**contentPixMap).bounds, &contentRect, srcCopy + ditherCopy, nil);    
  471.                 break;
  472.             }// case Horizontal
  473.  
  474.             case Vertical:
  475.             {
  476.                 // Draw now that things are ready to go:
  477.                 colorSlice = (unsigned short)(maxRGBComponentValue/height);// the amount to incr by for each slice of the gradient
  478.                 const short sliceHeight = 1;
  479.  
  480.                 for(sliceNum = 0; sliceNum < height; sliceNum++)
  481.                 {
  482.                     theColor.red = sliceNum * colorSlice;
  483.                     theColor.green = sliceNum * colorSlice;
  484.                     theColor.blue = sliceNum * colorSlice;
  485.                     ::RGBForeColor(&theColor);
  486.                     
  487.                     ::SetRect(&sliceRect, 0, sliceNum, width, sliceNum + sliceHeight);
  488.                     assert(ValidRect(sliceRect));
  489.  
  490.                     ::PaintRect(&sliceRect);    // paint the gray transform filter
  491.                 }
  492.                 // restore orig gworld
  493.                 ::SetGWorld(currentPort, nil);
  494.  
  495.                 ::RGBForeColor(&mStartColor);
  496.                 ::RGBBackColor(&mEndColor);
  497.  
  498.                 ::CopyBits((BitMap *)(*contentPixMap), (BitMap *)(*mPixOffscreenPixMap), 
  499.                             &(**contentPixMap).bounds, &contentRect, srcCopy + ditherCopy, nil);    
  500.                 break;
  501.             }// case Vertical
  502.  
  503.             case BothHV:    // towards center
  504.             {
  505.                 // Draw now that things are ready to go:
  506.                 unsigned short maxDimension = (unsigned short)(max(height,width))/2;
  507.                 colorSlice = maxRGBComponentValue/maxDimension;// the amount to incr by for each slice of the gradient
  508.  
  509.                 ::SetRect(&sliceRect, 0, 0, width, height);
  510.                 assert(ValidRect(sliceRect));
  511.  
  512.                 for(sliceNum = 0; sliceNum < maxDimension; sliceNum++)
  513.                 {
  514.                     theColor.red = sliceNum * colorSlice;
  515.                     theColor.green = sliceNum * colorSlice;
  516.                     theColor.blue = sliceNum * colorSlice;
  517.                     ::RGBForeColor(&theColor);
  518.  
  519.                     ::PaintRect(&sliceRect);    // paint the gray transform filter
  520.  
  521.                     // get ready for the next pass:                
  522.                     // shrink the rect till it goes to zero in both dimensions (but not less than zero)
  523.                     ::InsetRect(&sliceRect, sliceNum < width/2 ? 1 : 0, sliceNum < height/2 ? 1 : 0);
  524.                     assert(ValidRect(sliceRect));
  525.                 }
  526.                 // restore orig gworld
  527.                 ::SetGWorld(currentPort, nil);
  528.  
  529.                 ::RGBForeColor(&mStartColor);
  530.                 ::RGBBackColor(&mEndColor);
  531.  
  532.                 ::CopyBits((BitMap *)(*contentPixMap), (BitMap *)(*mPixOffscreenPixMap), 
  533.                             &(**contentPixMap).bounds, &contentRect, srcCopy + ditherCopy, nil);    
  534.                 break;
  535.             }// case BothHV
  536.  
  537.             case Corner:    // towards corner gradient
  538.             {
  539.                 // Draw now that things are ready to go:
  540.                 short radius = (short)(sqrt(height*height + width*width) + 1);
  541.                 ::SetRect(&sliceRect, -radius, -radius, radius, radius);
  542.                 
  543.                 colorSlice = maxRGBComponentValue/radius;// the amount to incr by for each slice of the gradient
  544.  
  545.                 for(sliceNum = 0; sliceNum < radius; sliceNum++)
  546.                 {
  547.                     theColor.red = sliceNum * colorSlice;
  548.                     theColor.green = sliceNum * colorSlice;
  549.                     theColor.blue = sliceNum * colorSlice;
  550.                     ::RGBForeColor(&theColor);
  551.                 
  552.                     ::PaintOval(&sliceRect);    // paint the gray transform filter
  553.  
  554.                     // get ready for the next pass:                
  555.                     // shrink the rect till it goes to zero in both dimensions (but not less than zero)
  556.                     ::InsetRect(&sliceRect, 1, 1);
  557.                     assert(ValidRect(sliceRect));
  558.                 }
  559.                 // restore orig gworld
  560.                 ::SetGWorld(currentPort, nil);
  561.  
  562.                 ::RGBForeColor(&mStartColor);
  563.                 ::RGBBackColor(&mEndColor);
  564.  
  565.                 ::CopyBits((BitMap *)(*contentPixMap), (BitMap *)(*mPixOffscreenPixMap), 
  566.                             &(**contentPixMap).bounds, &contentRect, srcCopy + ditherCopy, nil);    
  567.                 break;
  568.             }// case Corner
  569.             
  570.             case DiagDn:
  571.             {
  572.                 // create temp PixMap:
  573.                 short tempWidth = width * 2;    // make the width twice that of the content rect…
  574.                 short tempHeight = height;        // …the height stays the same
  575.                 GWorldPtr tempGWorld = nil;        // init cause were anal
  576.                 Rect tempRect = {0, 0, tempHeight, tempWidth};
  577.                 theErr = ::NewGWorld(&tempGWorld, targetDepth , &tempRect, 0, nil, 0);
  578.                 assert(theErr == noErr);
  579.                 if(theErr != noErr)
  580.                     throw CGradientError(GWORLD_ALLOCATION_ERROR, this);
  581.  
  582.                 // Switch to the content GWorld and lock the offscreen buffer in memory.
  583.                 ::SetGWorld(tempGWorld, nil);
  584.                 PixMapHandle tempPixMap = ::GetGWorldPixMap(tempGWorld);
  585.                 ::LockPixels(tempPixMap);
  586.                 HLock((Handle)tempPixMap);
  587.  
  588.                 ::RGBBackColor(&mEndColor);
  589.                 // pre erase the content so that it is ready to go:
  590.                 ::EraseRect(&tempRect);
  591.  
  592.                 // Draw now that things are ready to go:
  593.                 colorSlice = (unsigned short)(maxRGBComponentValue/width);// the amount to incr by for each slice of the gradient
  594.                 const short sliceWidth = 1;
  595.  
  596.                 for(sliceNum = 0; sliceNum <= width; sliceNum++)    // overpaint by one so that we overlap the two merging rects
  597.                 {
  598.                     theColor.red = sliceNum * colorSlice;
  599.                     theColor.green = sliceNum * colorSlice;
  600.                     theColor.blue = sliceNum * colorSlice;
  601.                     ::RGBForeColor(&theColor);
  602.  
  603.                     // do one stripe on the left…
  604.                     ::SetRect(&sliceRect, sliceNum, 0, sliceNum + sliceWidth, height);
  605.                     assert(ValidRect(sliceRect));
  606.                     ::PaintRect(&sliceRect);    // paint the gray transform filter
  607.  
  608.                     // …and one stripe on the right side of tempRect
  609.                     ::SetRect(&sliceRect, tempWidth - sliceNum, 0, tempWidth + sliceWidth - sliceNum, height);
  610.                     assert(ValidRect(sliceRect));
  611.                     ::PaintRect(&sliceRect);    // paint the gray transform filter
  612.                 }// for
  613.                 
  614.                 // skew rect by copying with offset growing while copying into content:
  615.                 // restore content gworld
  616.                 ::SetGWorld(currentPort, currentDevice);
  617.  
  618.                 ::RGBForeColor(&mStartColor);
  619.                 ::RGBBackColor(&mEndColor);
  620.                 
  621.                 Rect sliceOfTempRect = gZeroRect;
  622.                 Rect sliceOfContentRect = gZeroRect;
  623.                 double slopeRecip = (double)width / (double)height;
  624.                 short xOffset = 0;            // init to nada
  625.                 double sliceNumOffset = 0.0;// ditto
  626.  
  627.                 for(sliceNum = 0; sliceNum < height; sliceNum++, sliceNumOffset++)
  628.                 {
  629.                     // build the src rect:
  630.                     xOffset = (short)(sliceNumOffset * slopeRecip);
  631.                     ::SetRect(&sliceOfTempRect,  width - xOffset, sliceNum, tempWidth - xOffset,  sliceNum + sliceWidth);
  632.                     assert(ValidRect(sliceOfTempRect));
  633.  
  634.                     // build the target rect:
  635.                     ::SetRect(&sliceOfContentRect, 0, sliceNum, width, sliceNum + sliceWidth);
  636.                     assert(ValidRect(sliceOfContentRect));
  637.                 
  638.                     // copy the map one slice at a time while moving across the filter
  639.                     ::CopyBits((BitMap *)(*tempPixMap), (BitMap *)(*mPixOffscreenPixMap), 
  640.                                 &sliceOfTempRect, &sliceOfContentRect, srcCopy + ditherCopy, nil);
  641.                 }// for
  642.  
  643.                 ::UnlockPixels(tempPixMap);
  644.                 ::HUnlock((Handle)tempPixMap);
  645.                 ::DisposeGWorld(tempGWorld);
  646.  
  647.                 break;
  648.             }// case DiagDn
  649.  
  650.             case DiagUp:
  651.             {
  652.                 // create temp PixMap:
  653.                 short tempWidth = width * 2;    // make the width twice that of the content rect…
  654.                 short tempHeight = height;        // …the height stays the same
  655.                 GWorldPtr tempGWorld = nil;        // init cause were anal
  656.                 Rect tempRect = {0, 0, tempHeight, tempWidth};
  657.                 theErr = ::NewGWorld(&tempGWorld, targetDepth , &tempRect, 0, nil, 0);
  658.                 assert(theErr == noErr);
  659.                 if(theErr != noErr)
  660.                     throw CGradientError(GWORLD_ALLOCATION_ERROR, this);
  661.  
  662.                 // Switch to the content GWorld and lock the offscreen buffer in memory.
  663.                 ::SetGWorld(tempGWorld, nil);
  664.                 PixMapHandle tempPixMap = ::GetGWorldPixMap(tempGWorld);
  665.                 ::LockPixels(tempPixMap);
  666.                 ::HLock((Handle)tempPixMap);
  667.  
  668.                 ::RGBBackColor(&mEndColor);
  669.                 // pre erase the content so that it is ready to go:
  670.                 ::EraseRect(&tempRect);
  671.  
  672.  
  673.                 // Draw now that things are ready to go:
  674.                 colorSlice = (unsigned short)(maxRGBComponentValue/width);// the amount to incr by for each slice of the gradient
  675.                 const short sliceWidth = 1;
  676.  
  677.                 for(sliceNum = 0; sliceNum <= width; sliceNum++)    // overpaint by one so that we overlap the two merging rects
  678.                 {
  679.                     theColor.red = sliceNum * colorSlice;
  680.                     theColor.green = sliceNum * colorSlice;
  681.                     theColor.blue = sliceNum * colorSlice;
  682.                     ::RGBForeColor(&theColor);
  683.  
  684.                     // do one stripe on the left…
  685.                     ::SetRect(&sliceRect, sliceNum, 0, sliceNum + sliceWidth, height);
  686.                     assert(ValidRect(sliceRect));
  687.                     ::PaintRect(&sliceRect);    // paint the gray transform filter
  688.  
  689.                     // …and one stripe on the right side of tempRect
  690.                     ::SetRect(&sliceRect, tempWidth - sliceNum, 0, tempWidth + sliceWidth - sliceNum, height);
  691.                     assert(ValidRect(sliceRect));
  692.                     ::PaintRect(&sliceRect);    // paint the gray transform filter
  693.                 }// for
  694.                 
  695.                 // skew rect by copying with offset growing while copying into content:
  696.                 // restore content gworld
  697.                 ::SetGWorld(currentPort, currentDevice);
  698.  
  699.                 ::RGBForeColor(&mStartColor);
  700.                 ::RGBBackColor(&mEndColor);
  701.                 
  702.                 Rect sliceOfTempRect = gZeroRect;
  703.                 Rect sliceOfContentRect = gZeroRect;
  704.                 double slopeRecip = (double)width / (double)height;
  705.                 short xOffset = 0;            // init to nada
  706.                 double sliceNumOffset = 0.0;// ditto
  707.  
  708.                 for(sliceNum = 0; sliceNum < height; sliceNum++, sliceNumOffset++)
  709.                 {
  710.                     // build the src rect:
  711.                     xOffset = (short)(sliceNumOffset * slopeRecip);
  712.                     ::SetRect(&sliceOfTempRect,  xOffset, sliceNum, width + xOffset,  sliceNum + sliceWidth);
  713.                     assert(ValidRect(sliceOfTempRect));
  714.  
  715.                     // build the target rect:
  716.                     ::SetRect(&sliceOfContentRect, 0, sliceNum, width, sliceNum + sliceWidth);
  717.                     assert(ValidRect(sliceOfContentRect));
  718.                 
  719.                     // copy the map one slice at a time while moving across the filter
  720.                     ::CopyBits((BitMap *)(*tempPixMap), (BitMap *)(*mPixOffscreenPixMap), 
  721.                                 &sliceOfTempRect, &sliceOfContentRect, srcCopy + ditherCopy, nil);
  722.                 }// for
  723.  
  724.                 ::UnlockPixels(tempPixMap);
  725.                 ::HUnlock((Handle)tempPixMap);
  726.                 ::DisposeGWorld(tempGWorld);
  727.  
  728.                 break;
  729.             }// case DiagUp
  730.  
  731.             case PointBased:
  732.             case LineBased:
  733.                 ASSERT(0, "Not yet implemented!");
  734.  
  735.             default:
  736.                 break;
  737.         }// switch
  738.  
  739.         ::DisposeGWorld(contentGWorld);
  740.         
  741.     }// try
  742.         
  743.     // error handling
  744.     catch(CGradientError &gradientError)
  745.     {
  746.         gradientError.HandleError();
  747.     }
  748.             
  749. }
  750.  
  751.  
  752. // end-of-file ////////////////////////////////////////////////////////////////
  753.  
  754.